Skip to content

Exercises

Product Details page

Let's suppose we're building an e-commerce site. We received a mockup, and we implemented it as one big component.

In terms of our spectrum of abstraction, we've created a very high-level component, but there are two lower-level components hiding within, begging to be extracted into their own components.

Your mission is to identify a spectrum-related problem, and to extract at least 1 lower-level component.

Acceptance Criteria:

  • Extract at least 1 lower-level, potentially-reusable component
  • The extracted component(s) can be defined inside /ProductDetails.js, below the main component.
  • If you're not sure where to start / which components should be extracted, I provide my suggestions below the sandbox:

Code Playground

import React from 'react';
import { Star } from 'react-feather';

import { range } from './utils';
import VisuallyHidden from './VisuallyHidden';

function ProductDetails({ product }) {
const [
selectedPhotoIndex,
setSelectedPhotoIndex,
] = React.useState(0);

return (
<article className="product-details">
<div className="photos-wrapper">
<div>
<img
className="primary-photo"
alt=""
src={product.photos[selectedPhotoIndex]}
/>
<div className="buttons">
{product.photos.map((photoSrc, index) => {
const isSelected = selectedPhotoIndex === index;
return (
<button
key={index}
className="thumbnail-button"
onClick={() => setSelectedPhotoIndex(index)}
>
<VisuallyHidden>
Toggle image #{index + 1}
</VisuallyHidden>
<img alt="" src={photoSrc} />
<span
className="selected-ring"
style={{
opacity: isSelected ? 1 : 0,
}}
/>
</button>
);
})}
</div>
</div>
</div>
<div className="product-info">
<h1>{product.title}</h1>
<div className="star-rating">
{range(5).map((num) => {
const className =
product.rating > num
? 'star filled'
: 'star hollow';
return <Star className={className} />;
})}
</div>
<p className="product-description">
{product.description}
</p>
</div>
</article>
);
}

export default ProductDetails;
  1. Warning: Each child in a list should have a unique "key" prop. Check the render method of `ProductDetails`. See https://reactjs.org/link/warning-keys for more information. at eval (https://sandpack-bundler.vercel.app/node_modules/react-feather/dist/icons/star.js:51:25) at ProductDetails (https://sandpack-bundler.vercel.app/ProductDetails.js:26:3) at App

Solution:

Extracting a Card component

In the sandbox below, we have two separate components that each implement a similar "card" design. Let's extract it into its own Card component!

Acceptance Criteria:

  • Two files, Card.js and Card.module.css, have been created for you. Your mission is to populate them with the component + styles.
  • You should then use this component within UserProfileCard and ProductInfoCard.
  • Note that the two cards have slightly different styles: they have a different box-shadow value. Each card should be able to specify the elevation level for the shadow, to be applied dynamically inside Card.

Code Playground

import React from 'react';

import UserProfileCard from './UserProfileCard';
import ProductInfoCard from './ProductInfoCard';

function App() {
return (
<>
<UserProfileCard
user={{
avatarSrc: 'https://sandpack-bundler.vercel.app/img/avatars/009.png',
avatarDescription: 'Cartoon bear',
name: 'Ben Thorn',
handle: 'benjaminthorn',
}}
/>
<ProductInfoCard
product={{
id: 'hk123',
imageSrc: 'https://sandpack-bundler.vercel.app/img/shopping-cart-coffee-machine.jpg',
imageAlt: 'A pink drip coffee machine with the “Hello Kitty” logo',
title: '“Hello Kitty” Coffee Machine',
price: '89.99',
inStock: true,
}}
/>
</>
);
}

export default App;

Solution:

In this video, I use the “property value shorthand” when applying the boxShadow style. For more information, check out the “Property Value Shorthand” lesson 👀 in the JavaScript Primer.